package com.augurit.common.base.action;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import com.augurit.common.base.form.Attachment;
import com.augurit.common.base.form.ResultForm;
import com.augurit.common.constant.SystemConstant;
import com.augurit.common.sysfile.service.ISysFileService;
import com.augurit.common.sysfile.web.form.SysFileForm;
import com.augurit.common.util.ActionUtils;
import com.augurit.common.util.FileUtils;
import com.augurit.gzcgwsp.webservice.SFTPUtil;

/**
 * 附件上传基类
 */
public class BaseAttachmentAction extends BaseAction {
	protected final Logger logger=Logger.getLogger(BaseAttachmentAction.class);
	protected static final int BUFFER_SIZE = 2048;
	
	@Autowired
	protected ISysFileService sysFileService;

	//上传的附件
	protected File[] attachment;
	protected String[] attachmentContentType;
	protected String[] attachmentFileName;
	
	
	/**
	 * 获取上传的附件对象列表
	 * @return 附件对象列表
	 */
	public List<Attachment> getAttachmentList(){
		if(attachment != null && attachment.length > 0){
			List list = new ArrayList();
			
			for(int i=0; i<attachment.length; i++){
				Attachment form = new Attachment();
				form.setFile(attachment[i]);
				form.setFileContentType(attachmentContentType[i]);
				form.setFileFileName(attachmentFileName[i]);
				list.add(form);
			}
			
			return list;
		}
		return null;
	}
	
	/**
	 * 将附件上传至服务器磁盘的同时向附件表插入附件记录
	 * @param entity 业务表对应的VO对象名
	 * @param entityId 业务表记录主键
	 * @param filePath 附件上传路径
	 * @param fileCode 附件编号，如为null则自动以时间作为编号
	 * @param fileName 附件名称，如为null则使用fileForm对象的文件名属性值
	 * @param fileType 附件类型，允许为null
	 * @param attachment 附件对象
	 * @return
	 */
	public boolean upload(String entity, Long entityId, String filePath, String fileCode, String fileName, String fileType, Attachment attachment){
		
		//构造Form对象
		SysFileForm form = this.buildNewSysFileForm(entity, entityId, filePath, fileCode, fileName, fileType, attachment);
		
		return upload(form, attachment);
	}
	
	/**
	 * 将附件上传至服务器磁盘的同时向附件表插入附件记录
	 * @param form 附件Form对象
	 * @param filePath 附件上传路径
	 * @param attachment 附件对象
	 * @return
	 */
	public boolean upload(SysFileForm form, Attachment attachment){
		boolean result = false;
		
		if(form != null){
			
			//将附件上传至服务器磁盘的指定路径下
			boolean success = uploadFile(form.getFilePath(), form.getFileCode() +"."+form.getFileFormat(), attachment, false, true, true);

			//更新附件记录
			if(success){
				//当附件内容被修改时需要更新Form对象的相关属性
				form.setFileSize(attachment.getFile().length());
				updateChangeStatus(form);
				sysFileService.save(form, this.getLoginUser().getUser().getUserName());
				result = true;
			}
		}
		
		return result;
	}
	
	/**
	 * 将附件上传至服务器磁盘的同时向附件表插入附件记录
	 * @param entity 业务表对应的VO对象名
	 * @param entityId 业务表记录主键
	 * @param filePath 附件上传路径
	 * @param fileCodes 附件编号列表。如设为null则使用自动编号
	 * @param fileNames 附件名称列表。如设为null则使用原始文件名
	 * @param fileTypes 附件类型列表
	 * @param attachments 附件对象列表
	 * @return
	 */
	public boolean upload(String entity, Long entityId, String filePath, List<String> fileCodes, List<String> fileNames, List<String> fileTypes, List<Attachment> attachments){
		boolean result = false;
		
		if(entity != null && entity.trim().length() > 0 && entityId != null
				&& filePath != null && filePath.trim().length() > 0 && attachments != null && attachments.size() > 0){
			
			boolean existFileCodes = fileCodes != null && fileCodes.size() > 0 ? true : false;	//是否设置附件编号
			boolean existFileNames = fileNames != null && fileNames.size() > 0 ? true : false;	//是否设置附件名称
			boolean existFileTypes = fileTypes != null && fileTypes.size() > 0 ? true : false;	//是否设置附件类型
			
			for(int i=0; i<attachments.size(); i++){
				
				SysFileForm form = this.buildNewSysFileForm(entity, entityId, 
						existFileCodes ? fileCodes.get(i) : null, 
						existFileNames ? fileNames.get(i) : null, 
						existFileTypes ? fileTypes.get(i) : null, 
						filePath, attachments.get(i));
				
				this.upload(form, attachments.get(i));
			}
			
			result = true;
		}
		
		return result;
	}
	
	/**
	 * 删除附件记录及其服务器磁盘上的物理文件
	 * @param sysFileId 附件对象ID
	 * @return true表示删除成功，false表示删除失败
	 */
	public boolean delete(Long sysFileId){
		
		//获取附件对象
		SysFileForm form = sysFileService.get(sysFileId);

		return delete(form);
	}
	
	/**
	 * 删除附件记录及其服务器磁盘上的物理文件
	 * @param entity 业务表对应的VO对象名
	 * @param entityId 业务表记录主键
	 * @return true表示删除成功，false表示删除失败
	 */
	public void delete(String entity, Long entityId){

		//获取附件对象列表
		List<SysFileForm> list = sysFileService.getByEntityAndEntityId(entity, entityId);

		if(list != null && list.size() > 0){
			for(SysFileForm form : list)
				delete(form);
		}
		
	}
	
	/**
	 * 删除附件记录及其服务器磁盘上的物理文件
	 * @param form 附件对象
	 * @return true表示删除成功，false表示删除失败
	 */
	public boolean delete(SysFileForm form){
		boolean result = false;
		
		if(form != null){
			
			//删除服务器磁盘上的附件文件
			boolean success = deleteFile(form.getFilePath(), form.getFileCode() + "." + form.getFileFormat(), false);
			
			//级联删除附件记录
			if(success){
				sysFileService.deleteCascade(form);
				result = true;
			}
		}
		
		return result;
	}
	

	/**
	 * 保存附件（保存附件的内容，并使数据库中附件信息与磁盘上附件信息同步）
	 * @param form 附件表单
	 * @param attachment 附件
	 * @return
	 */
	public ResultForm saveFormAndAttachment(SysFileForm form, Attachment attachment)throws Exception{
		
		ResultForm result = new ResultForm(true);
		
		//新建的文档或者文档的内容修改了
		if(form.getIsAttachmentContentChange()){
			boolean success = this.uploadFile(form.getFilePath(), form.getFileCode()+"."+form.getFileFormat(), attachment, false, true, true);
			
			if(!success){
				ActionUtils.setErrMsgOfResult(result, "上传附件内容失败！");
				return result;
			}

			form.setFileSize(attachment.getFile().length());
		}
		
		//判断修改的文件名是否合法
		if(!FileUtils.isFileNameValid(form.getNewFileName())){
			ActionUtils.setErrMsgOfResult(result, "文件名不合法！文件名中包含\\/<> * ？等非法字符") ;
			return result;
		}
		
		form.setFilePath(FileUtils.formatPath(form.getFilePath()));
		form.setNewFilePath(FileUtils.formatPath(form.getNewFilePath()));
		//文件路径或者文件名改变了，同步更新磁盘文件
		if(!FileUtils.isTwoPathSame(form.getFilePath(), form.getFileName(), form.getNewFilePath(), form.getNewFileName(), false)){
			
			String directoryPath = this.getApplication().getRealPath("/");
			boolean success = FileUtils.renameFile(form.getFilePath(), form.getFileName(), form.getNewFilePath(), form.getNewFileName(), false, directoryPath, true, false);
			if(!success){
				ActionUtils.setErrMsgOfResult(result, "修改文件名或者移动文件失败") ;
				return result;
			}
		}
		
		//当附件内容被修改时需要更新Form对象的相关属性
		updateChangeStatus(form);
		sysFileService.save(form);
		result.setErrMsg("已成功保存附件！");
		return result;
	}
	
	/**
	 * 将附件上传至服务器磁盘的指定路径下
	 * @param filePath 附件上传的服务器磁盘路径
	 * @param fileName 文件名
	 * @param attachment 页面上传的附件对象
	 * @param isAbsoluteFilePath 是否为绝对路径。true表示为filePath为服务器的绝对路径，false表示为当前应用下的相对路径
	 * @param autoBuildDir true表示当文件路径不存在时自动创建
	 * @param canCover true表示当指定文件已经存在时可覆盖原有文件
	 * @return true表示上传成功，false表示上传失败
	 */
	public boolean uploadFile(String filePath, String fileName, Attachment attachment, boolean isAbsoluteFilePath, boolean autoBuildDir, boolean canCover) {
		boolean result = false;
		
		if(attachment == null || attachment.getFile() == null 
				|| attachment.getFile().length() == 0 || filePath == null || filePath.trim().length() == 0)
			return result;		

		InputStream in = null;
		OutputStream ou = null;

		try {
			// 文件路径格式处理
			//filePath = filePath.replace('/', '\\');
		    filePath = filePath.replace('\\','/');

			// 上传附件文件夹路径
			//String directoryPath = (isAbsoluteFilePath ? "" : this.getApplication().getRealPath("\\")) + filePath;
		    String directoryPath = (isAbsoluteFilePath ? "" : this.getApplication().getRealPath("/")) + filePath;

			// 完整上传路径
			//String wholeFilePath = directoryPath + "\\" + fileName;
		    String wholeFilePath = directoryPath + "/" + fileName;

			// 如果文件目录不存在则创建
			if(autoBuildDir){
				File dir = new File(directoryPath);
				if(!dir.exists())
					dir.mkdirs();
			}

			// 输出附件
			File outputFile = new File(wholeFilePath);
				
			//如果文件不存在则可上传，如果文件存在并且设置为可覆盖则也可上传
			boolean isFileExist = outputFile.exists();
			if(!isFileExist || (isFileExist && canCover)){
					
				boolean canUpload = true; //标识是否能够上传文件
				
				//如果文件存在要先删除，如果文件不存在则标识为可上传
				if(isFileExist)
					canUpload = outputFile.delete();
				
				if(canUpload){
					// 保存附件至服务器
					in = new BufferedInputStream(new FileInputStream(attachment.getFile()), BUFFER_SIZE);
					ou = new BufferedOutputStream(new FileOutputStream(outputFile), BUFFER_SIZE);
					byte[] buffer = new byte[BUFFER_SIZE];
					while (in.read(buffer) > 0) {
						ou.write(buffer);
					}
					
					result = true;		//上传附件成功
				}
			}
			//同时上传一份到另外一台服务器
			SFTPUtil.uploadOtherFile(filePath+"/"+fileName, wholeFilePath);
			logger.info("附件上传到另外一台机器---------->"+wholeFilePath);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(in != null) {
					in.close();
				}
				if(ou != null) {
					ou.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}
	
	/**
	 * 删除服务器磁盘上的物理文件
	 * @param filePath 附件上传的服务器磁盘路径
	 * @param fileName 文件名（含后缀名）
	 * @param isAbsoluteFilePath 是否为绝对路径。true表示为filePath为服务器的绝对路径，false表示为当前应用下的相对路径
	 * @return true表示删除附件成功或附件不存在，false表示删除附件失败
	 */
	public boolean deleteFile(String filePath, String fileName, boolean isAbsoluteFilePath){
		boolean result = false;
		
		InputStream in = null;
		OutputStream ou = null;
		
		try{
			if(filePath != null && filePath.trim().length() > 0
					&& fileName != null && fileName.trim().length() > 0){
				
				// 文件路径格式处理
				filePath = filePath.replace('/', '\\');

				// 完整上传路径
				String wholeFilePath = isAbsoluteFilePath ? "" : this.getApplication().getRealPath("\\") + filePath + "\\" + fileName;
				
				File file = new File(wholeFilePath);
				
				//如果文件存在则删除
				if(file.exists())
					result = file.delete();
				else
					result = true;
			}
		}catch(Exception e){
			e.printStackTrace();
		} finally {
			try {
				if(in != null) {
					in.close();
				}
				if(ou != null) {
					ou.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}

	/**
	 * 构造SysFileForm对象
	 * @param entity
	 * @param entityId
	 * @param fileCode
	 * @param fileName
	 * @param fileType
	 * @param filePath
	 * @param attachment
	 * @return
	 */
	private SysFileForm buildNewSysFileForm(String entity, Long entityId, String filePath, String fileCode, String fileName, String fileType, Attachment attachment){
		SysFileForm form = new SysFileForm();
		form.setEntity(entity);
		form.setEntityId(entityId);
		form.setFileCode(fileCode != null && fileCode.trim().length() > 0 ? fileCode : new Date().getTime() + "");
		form.setFileName(fileName != null && fileName.trim().length() > 0 ? fileName : attachment.getFileFileName());
		form.setFilePath(filePath != null  && filePath.trim().length() > 0 ? filePath : SystemConstant.DEFAULT_ATTACHMENT_FILEPATH);
		form.setFileType(fileType);
		form.setFileSize(attachment.getFile().length());
		form.setFileFormat(FileUtils.getFileFormat(form.getFileName()));
		return form;
	}

	/**
	 * 当附件内容被修改时需要更新Form对象的相关属性
	 * @param form
	 * @param attachment
	 */
	public void updateChangeStatus(SysFileForm form){
		if(form != null && form.getSysFileId() != null){
			form.setEemp(this.getLoginUser().getUser().getUserName());
			form.setEdt(new Date());
			form.setFilePath(form.getNewFilePath() != null ? form.getNewFilePath() : form.getFilePath());
			form.setFileName(form.getNewFileName() != null ? form.getNewFileName() : form.getFileName());
		}
	}
	
	
	public File[] getAttachment() {
		return attachment;
	}

	public void setAttachment(File[] attachment) {
		this.attachment = attachment;
	}

	public String[] getAttachmentContentType() {
		return attachmentContentType;
	}

	public void setAttachmentContentType(String[] attachmentContentType) {
		this.attachmentContentType = attachmentContentType;
	}

	public String[] getAttachmentFileName() {
		return attachmentFileName;
	}

	public void setAttachmentFileName(String[] attachmentFileName) {
		this.attachmentFileName = attachmentFileName;
	}
}
